summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
new file mode 100644
index 000000000..97eef40d2
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -0,0 +1,165 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.MarginLayoutParams
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.updatePadding
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import com.google.android.material.color.MaterialColors
+import com.google.android.material.transition.MaterialFadeThrough
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.adapters.GameAdapter
+import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
+import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
+import org.yuzu.yuzu_emu.model.GamesViewModel
+import org.yuzu.yuzu_emu.model.HomeViewModel
+
+class GamesFragment : Fragment() {
+ private var _binding: FragmentGamesBinding? = null
+ private val binding get() = _binding!!
+
+ private val gamesViewModel: GamesViewModel by activityViewModels()
+ private val homeViewModel: HomeViewModel by activityViewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enterTransition = MaterialFadeThrough()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentGamesBinding.inflate(inflater)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ homeViewModel.setNavigationVisibility(visible = true, animated = false)
+
+ binding.gridGames.apply {
+ layoutManager = AutofitGridLayoutManager(
+ requireContext(),
+ requireContext().resources.getDimensionPixelSize(R.dimen.card_width)
+ )
+ adapter = GameAdapter(requireActivity() as AppCompatActivity)
+ }
+
+ binding.swipeRefresh.apply {
+ // Add swipe down to refresh gesture
+ setOnRefreshListener {
+ gamesViewModel.reloadGames(false)
+ }
+
+ // Set theme color to the refresh animation's background
+ setProgressBackgroundColorSchemeColor(
+ MaterialColors.getColor(
+ binding.swipeRefresh,
+ com.google.android.material.R.attr.colorPrimary
+ )
+ )
+ setColorSchemeColors(
+ MaterialColors.getColor(
+ binding.swipeRefresh,
+ com.google.android.material.R.attr.colorOnPrimary
+ )
+ )
+
+ // Make sure the loading indicator appears even if the layout is told to refresh before being fully drawn
+ post {
+ if (_binding == null) {
+ return@post
+ }
+ binding.swipeRefresh.isRefreshing = gamesViewModel.isReloading.value!!
+ }
+ }
+
+ gamesViewModel.apply {
+ // Watch for when we get updates to any of our games lists
+ isReloading.observe(viewLifecycleOwner) { isReloading ->
+ binding.swipeRefresh.isRefreshing = isReloading
+ }
+ games.observe(viewLifecycleOwner) {
+ (binding.gridGames.adapter as GameAdapter).submitList(it)
+ if (it.isEmpty()) {
+ binding.noticeText.visibility = View.VISIBLE
+ } else {
+ binding.noticeText.visibility = View.GONE
+ }
+ }
+ shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
+ if (shouldSwapData) {
+ (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
+ gamesViewModel.setShouldSwapData(false)
+ }
+ }
+
+ // Check if the user reselected the games menu item and then scroll to top of the list
+ shouldScrollToTop.observe(viewLifecycleOwner) { shouldScroll ->
+ if (shouldScroll) {
+ scrollToTop()
+ gamesViewModel.setShouldScrollToTop(false)
+ }
+ }
+ }
+
+ setInsets()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ private fun scrollToTop() {
+ if (_binding != null) {
+ binding.gridGames.smoothScrollToPosition(0)
+ }
+ }
+
+ private fun setInsets() =
+ ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
+ val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+ val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
+ val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large)
+ val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)
+ val spacingNavigationRail =
+ resources.getDimensionPixelSize(R.dimen.spacing_navigation_rail)
+
+ binding.gridGames.updatePadding(
+ top = barInsets.top + extraListSpacing,
+ bottom = barInsets.bottom + spacingNavigation + extraListSpacing
+ )
+
+ binding.swipeRefresh.setProgressViewEndTarget(
+ false,
+ barInsets.top + resources.getDimensionPixelSize(R.dimen.spacing_refresh_end)
+ )
+
+ val leftInsets = barInsets.left + cutoutInsets.left
+ val rightInsets = barInsets.right + cutoutInsets.right
+ val mlpSwipe = binding.swipeRefresh.layoutParams as MarginLayoutParams
+ if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ mlpSwipe.leftMargin = leftInsets + spacingNavigationRail
+ mlpSwipe.rightMargin = rightInsets
+ } else {
+ mlpSwipe.leftMargin = leftInsets
+ mlpSwipe.rightMargin = rightInsets + spacingNavigationRail
+ }
+ binding.swipeRefresh.layoutParams = mlpSwipe
+
+ binding.noticeText.updatePadding(bottom = spacingNavigation)
+
+ windowInsets
+ }
+}